package com.yanyeori.framework.datasource.builder;

import com.alibaba.druid.pool.DruidDataSource;
import com.yanyeori.framework.datasource.config.DruidDataAccessAutoConfig;
import com.yanyeori.framework.datasource.configprop.DataSourceConfigProps;
import com.yanyeori.framework.datasource.configprop.MultiDataSourceConfigProps;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Import;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;

/**
 * Druid DataSource Builder
 *
 * @author chenkuan 2021/4/16
 */
@Slf4j
@ConditionalOnClass(DruidDataSource.class)
@Import(DruidDataAccessAutoConfig.class)
public class DruidDataSourceBuilder implements DataSourceBuilder {

    @Override
    public DataSource build(DataSourceConfigProps props, MultiDataSourceConfigProps basicProps) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(basicProps.getUrl());
        dataSource.setUsername(basicProps.getUsername());
        dataSource.setPassword(basicProps.getPassword());
        dataSource.setDriverClassName(basicProps.getDriverClassName());
        druidPoolConfig(dataSource, props);
        return dataSource;
    }

    @Override
    public Class<? extends DataSource> getType() {
        return DruidDataSource.class;
    }

    /**
     * Druid连接池相关配置
     */
    private void druidPoolConfig(DruidDataSource dataSource, DataSourceConfigProps props) {
        DruidConfigProps config = props.getDruid();
        dataSource.setInitialSize(config.getInitialSize());
        dataSource.setMinIdle(config.getMinIdle());
        dataSource.setMaxActive(config.getMaxActive());
        dataSource.setMaxWait(config.getMaxWait());
        dataSource.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis());
        dataSource.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis());
        dataSource.setMaxEvictableIdleTimeMillis(config.getMaxEvictableIdleTimeMillis());
        dataSource.setValidationQuery(config.getValidationQuery());
        dataSource.setValidationQueryTimeout(config.getValidationQueryTimeout());
        dataSource.setTestWhileIdle(config.isTestWhileIdle());
        dataSource.setTestOnBorrow(config.isTestOnBorrow());
        dataSource.setTestOnReturn(config.isTestOnReturn());
        dataSource.setPoolPreparedStatements(config.isPoolPreparedStatements());
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(config.getMaxPoolPreparedStatementPerConnectionSize());
        dataSource.setSharePreparedStatements(config.isSharePreparedStatements());
        try {
            dataSource.setFilters(config.getFilters());
        } catch (SQLException throwables) {
            log.warn("The Druid connection pool [Filters] configuration failed");
        }
        dataSource.setMaxOpenPreparedStatements(config.getMaxOpenPreparedStatements());
        dataSource.setUseGlobalDataSourceStat(config.isUseGlobalDataSourceStat());
        dataSource.setDefaultAutoCommit(config.isDefaultAutoCommit());
        dataSource.setDefaultReadOnly(config.getDefaultReadOnly());
        dataSource.setDefaultTransactionIsolation(config.getDefaultTransactionIsolation());
        dataSource.setDefaultCatalog(config.getDefaultCatalog());
        dataSource.setNotFullTimeoutRetryCount(config.getNotFullTimeoutRetryCount());
        dataSource.setInitExceptionThrow(config.isInitExceptionThrow());
        dataSource.setClearFiltersEnable(config.isClearFiltersEnable());
        try {
            dataSource.setExceptionSorter(config.getExceptionSorter());
        } catch (SQLException e) {
            log.warn("The Druid connection pool [ExceptionSorter] configuration failed");
        }
        dataSource.setConnectTimeout(config.getConnectTimeout());
        dataSource.setSocketTimeout(config.getSocketTimeout());
        dataSource.setQueryTimeout(config.getQueryTimeout());
        dataSource.setTransactionQueryTimeout(config.getTransactionQueryTimeout());
        dataSource.setMaxWaitThreadCount(config.getMaxWaitThreadCount());
        dataSource.setAccessToUnderlyingConnectionAllowed(config.isAccessToUnderlyingConnectionAllowed());
        dataSource.setKeepAliveBetweenTimeMillis(config.getKeepAliveBetweenTimeMillis());
        dataSource.setPhyTimeoutMillis(config.getPhyTimeoutMillis());
        dataSource.setPhyMaxUseCount(config.getPhyMaxUseCount());
        dataSource.setRemoveAbandoned(config.isRemoveAbandoned());
        dataSource.setRemoveAbandonedTimeoutMillis(config.getRemoveAbandonedTimeoutMillis());
        dataSource.setLogAbandoned(config.isLogAbandoned());
        dataSource.setConnectionInitSqls(config.getConnectionInitSqls());
        dataSource.setTimeBetweenConnectErrorMillis(config.getTimeBetweenConnectErrorMillis());
        dataSource.setUsePingMethod(config.isUsePingMethod());
        dataSource.setConnectionErrorRetryAttempts(config.getConnectionErrorRetryAttempts());
        dataSource.setBreakAfterAcquireFailure(config.isBreakAfterAcquireFailure());
        dataSource.setTransactionThresholdMillis(config.getTransactionThresholdMillis());
        dataSource.setDupCloseLogEnable(config.isDupCloseLogEnable());
        dataSource.setOracle(config.isOracle());
        dataSource.setUseOracleImplicitCache(config.isUseOracleImplicitCache());
        dataSource.setUseUnfairLock(config.getUseUnfairLock());
        dataSource.setUseLocalSessionState(config.isUseLocalSessionState());
        dataSource.setTimeBetweenLogStatsMillis(config.getTimeBetweenLogStatsMillis());
        dataSource.setAsyncCloseConnectionEnable(config.isAsyncCloseConnectionEnable());
        dataSource.setMaxCreateTaskCount(config.getMaxCreateTaskCount());
        dataSource.setFailFast(config.isFailFast());
        dataSource.setInitVariants(config.isInitVariants());
        dataSource.setInitGlobalVariants(config.isInitGlobalVariants());
        dataSource.setOnFatalErrorMaxActive(config.getOnFatalErrorMaxActive());
    }

    /**
     * Druid连接池属性
     */
    @Getter
    @Setter
    public static class DruidConfigProps {
        private int initialSize = 1;
        private int minIdle = 1;
        private int maxActive = 10;
        private long maxWait = -1;
        private long timeBetweenEvictionRunsMillis = 60 * 1000L;
        private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
        private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;
        private String validationQuery;
        private int validationQueryTimeout = -1;
        private boolean testWhileIdle = true;
        private boolean testOnBorrow = false;
        private boolean testOnReturn = false;
        private boolean poolPreparedStatements = false;
        private int maxPoolPreparedStatementPerConnectionSize = 20;
        private boolean sharePreparedStatements = false;
        private String filters = "stat,wall";
        private int maxOpenPreparedStatements = -1;
        private boolean useGlobalDataSourceStat = true;
        protected boolean defaultAutoCommit = true;
        protected Boolean defaultReadOnly;
        protected Integer defaultTransactionIsolation;
        protected String defaultCatalog;
        protected int notFullTimeoutRetryCount;
        protected boolean initExceptionThrow;
        private boolean clearFiltersEnable;
        protected String exceptionSorter;
        protected int connectTimeout = 60 * 1000;
        protected int socketTimeout;
        protected int queryTimeout;
        protected int transactionQueryTimeout;
        protected int maxWaitThreadCount;
        protected boolean accessToUnderlyingConnectionAllowed;
        protected long keepAliveBetweenTimeMillis;
        protected long phyTimeoutMillis;
        protected long phyMaxUseCount;
        protected boolean removeAbandoned;
        protected long removeAbandonedTimeoutMillis;
        protected boolean logAbandoned;
        protected List<String> connectionInitSqls;
        protected long timeBetweenConnectErrorMillis;
        protected boolean usePingMethod;
        protected int connectionErrorRetryAttempts;
        protected boolean breakAfterAcquireFailure;
        protected long transactionThresholdMillis;
        private boolean dupCloseLogEnable;
        protected boolean oracle;
        protected boolean useOracleImplicitCache;
        private Boolean useUnfairLock;
        private boolean useLocalSessionState;
        protected long timeBetweenLogStatsMillis;
        protected boolean asyncCloseConnectionEnable;
        protected int maxCreateTaskCount;
        protected boolean failFast;
        protected boolean initVariants;
        protected boolean initGlobalVariants;
        protected int onFatalErrorMaxActive;
    }
}
